module nade.primitive;

import std.math;

import derelict.opengl.gl;

import nade.vector;
import nade.angle;


void renderPrimitive(GLenum type, Vector2[] points ...)
{
	glBegin(type);
	foreach(point; points) glVertex2f(point.x, point.y);
	glEnd();
}

void renderPrimitive3D(GLenum type, Vector3[] points ...)
{
	glBegin(type);
	foreach(point; points) glVertex3f(point.x, point.y, point.z);
	glEnd();
}


void renderLine(Vector2 p1, Vector2 p2)
{
	renderPrimitive(GL_LINES, p1, p2);
}

void renderLineStrip(Vector2[] points ...)
{
	renderPrimitive(GL_LINE_STRIP, points);
}

void renderLineLoop(Vector2[] points ...)
{
	renderPrimitive(GL_LINE_LOOP, points);
}


void renderTriangle(Vector2 p1, Vector2 p2, Vector2 p3)
{
	renderPrimitive(GL_LINE_LOOP, p1, p2, p3);
}

void renderFilledTriangle(Vector2 p1, Vector2 p2, Vector2 p3)
{
	renderPrimitive(GL_TRIANGLES, p1, p2, p3);
}


void renderRectangle(Vector2 from, Vector2 to)
{
	renderPrimitive(GL_LINE_LOOP, from, Vector2(to.x, from.y), to, Vector2(from.x, to.y));
}

void renderFilledRectangle(Vector2 from, Vector2 to)
{
	renderPrimitive(GL_QUADS, from, Vector2(to.x, from.y), to, Vector2(from.x, to.y));
}


void renderCircle(const Vector2 center, real radius, int res)
{
	const delta = 2*PI/res;

	glBegin(GL_LINE_LOOP);
	for(real a = 0;  a < 2*PI;  a += delta) glVertex2f(center.x + sin(a)*radius, center.y + cos(a)*radius);
	glEnd();
}

void renderFilledCircle(const Vector2 center, real radius, int res)
{
	const delta = 2*PI/res;

	glBegin(GL_POLYGON);
	for(real a = 0;  a < 2*PI;  a += delta) glVertex2f(center.x + sin(a)*radius, center.y + cos(a)*radius);
	glEnd();
}


void renderCircleSegment(F, T)(const Vector2 center, double radius, F from, T to, int res)
{
	const delta = (to.radians - from.radians)/res;

	glBegin(GL_LINE_LOOP);
	for(int i = 0;  i <= res;  ++i)
	{
		const a = from.radians + i*delta;
		glVertex2f(center.x + sin(a)*radius, center.y + cos(a)*radius);
	}
	glEnd();
}

void renderFilledCircleSegment(F, T)(const Vector2 center, double radius, F from, T to, int res)
{
	const delta = (to.radians - from.radians)/res;

	glBegin(GL_POLYGON);
	for(int i = 0;  i <= res;  ++i)
	{
		const a = from.radians + i*delta;
		glVertex2f(center.x + sin(a)*radius, center.y + cos(a)*radius);
	}
	glEnd();
}


void renderCircleSector(F, T)(const Vector2 center, double radius, F from, T to, int res)
{
	const delta = (to.radians - from.radians)/res;

	glBegin(GL_LINE_LOOP);
	center.render();
	for(int i = 0;  i <= res;  ++i)
	{
		const a = from.radians + i*delta;
		glVertex2f(center.x + sin(a)*radius, center.y + cos(a)*radius);
	}
	glEnd();
}

void renderFilledCircleSector(F, T)(const Vector2 center, double radius, F from, T to, int res)
{
	const delta = (to.radians - from.radians)/res;

	glBegin(GL_TRIANGLE_FAN);
	center.render();
	for(int i = 0;  i <= res;  ++i)
	{
		const a = from.radians + i*delta;
		glVertex2f(center.x + sin(a)*radius, center.y + cos(a)*radius);
	}
	glEnd();
}


void renderCircleArc(F, T)(const Vector2 center, double radius, F from, T to, int res)
{
	const delta = (to.radians - from.radians)/res;

	glBegin(GL_LINE_STRIP);
	for(int i = 0;  i <= res;  ++i)
	{
		const a = from.radians + i*delta;
		glVertex2f(center.x + sin(a)*radius, center.y + cos(a)*radius);
	}
	glEnd();
}


void renderEllipse(const Vector2 center, Vector2 radius, int res)
{
	const delta = 2*PI/res;

	glBegin(GL_LINE_LOOP);
	for(real a = 0;  a < 2*PI;  a += delta) glVertex2f(center.x + sin(a)*radius.x, center.y + cos(a)*radius.y);
	glEnd();
}

void renderFilledEllipse(const Vector2 center, Vector2 radius, int res)
{
	const delta = 2*PI/res;

	glBegin(GL_POLYGON);
	for(real a = 0;  a < 2*PI;  a += delta) glVertex2f(center.x + sin(a)*radius.x, center.y + cos(a)*radius.y);
	glEnd();
}


void renderLine(const Vector3 p1, const Vector3 p2)
{
	renderPrimitive3D(GL_LINES, p1.dup, p2.dup);
}


void renderBox(const Vector3 from, const Vector3 to)
{
	glBegin(GL_LINE_LOOP);
	glVertex3f(from.x, from.y, from.z);
	glVertex3f(from.x, from.y, to.z);
	glVertex3f(from.x, to.y, to.z);
	glVertex3f(from.x, to.y, from.z);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glVertex3f(to.x, to.y, from.z);
	glVertex3f(to.x, to.y, to.z);
	glVertex3f(to.x, from.y, to.z);
	glVertex3f(to.x, from.y, from.z);
	glEnd();

	glBegin(GL_LINES);
	glVertex3f(from.x, from.y, from.z);
	glVertex3f(to.x, from.y, from.z);
	glVertex3f(from.x, from.y, to.z);
	glVertex3f(to.x, from.y, to.z);
	glVertex3f(from.x, to.y, from.z);
	glVertex3f(to.x, to.y, from.z);
	glVertex3f(from.x, to.y, to.z);
	glVertex3f(to.x, to.y, to.z);
	glEnd();
}

void renderSolidBox(const Vector3 from, const Vector3 to)
{
	glBegin(GL_QUADS);
	glNormal3f(-1, 0, 0);
	glTexCoord2f(0, 0);							glVertex3f(from.x, from.y, from.z);
	glTexCoord2f(0, to.z - from.z);				glVertex3f(from.x, from.y, to.z);
	glTexCoord2f(to.y - from.y, to.z - from.z);	glVertex3f(from.x, to.y, to.z);
	glTexCoord2f(to.y - from.y, 0);				glVertex3f(from.x, to.y, from.z);

	glNormal3f(1, 0, 0);
	glTexCoord2f(to.y - from.y, 0);				glVertex3f(to.x, to.y, from.z);
	glTexCoord2f(to.y - from.y, to.z - from.z);	glVertex3f(to.x, to.y, to.z);
	glTexCoord2f(0, to.z - from.z);				glVertex3f(to.x, from.y, to.z);
	glTexCoord2f(0, 0);							glVertex3f(to.x, from.y, from.z);

	glNormal3f(0, -1, 0);
	glTexCoord2f(0, 0);							glVertex3f(from.x, from.y, from.z);
	glTexCoord2f(to.x - from.x, 0);				glVertex3f(to.x, from.y, from.z);
	glTexCoord2f(to.x - from.x, to.z - from.z);	glVertex3f(to.x, from.y, to.z);
	glTexCoord2f(0, to.z - from.z);				glVertex3f(from.x, from.y, to.z);

	glNormal3f(0, 1, 0);
	glTexCoord2f(0, to.z - from.z);				glVertex3f(from.x, to.y, to.z);
	glTexCoord2f(to.x - from.x, to.z - from.z);	glVertex3f(to.x, to.y, to.z);
	glTexCoord2f(to.x - from.x, 0);				glVertex3f(to.x, to.y, from.z);
	glTexCoord2f(0, 0);							glVertex3f(from.x, to.y, from.z);

	glNormal3f(0, 0, -1);
	glTexCoord2f(0, to.y - from.y);				glVertex3f(from.x, to.y, from.z);
	glTexCoord2f(to.x - from.x, to.y - from.y);	glVertex3f(to.x, to.y, from.z);
	glTexCoord2f(to.x - from.x, 0);				glVertex3f(to.x, from.y, from.z);
	glTexCoord2f(0, 0);							glVertex3f(from.x, from.y, from.z);

	glNormal3f(0, 0, 1);
	glTexCoord2f(0, 0);							glVertex3f(from.x, from.y, to.z);
	glTexCoord2f(to.x - from.x, 0);				glVertex3f(to.x, from.y, to.z);
	glTexCoord2f(to.x - from.x, to.y - from.y);	glVertex3f(to.x, to.y, to.z);
	glTexCoord2f(0, to.y - from.y);				glVertex3f(from.x, to.y, to.z);
	glEnd();
}
